#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "base.h"
#include "service.h"
#include "msg.h"
#include "http.h"
#include "cfg.h"
#include "hidb.h"

#define MODULE	"SEARCH_LOADER_V001"

#define USAGE		"Usage: %s [-vh] [-f <filename>] ..."		\
 " \nOptions:"													\
 " \n  -h            Show Usage"								\
 " \n  -v            Show Version"								\
 " \n  -f filename   Specify a Configure file"					\
 " \n\n"

typedef struct TASK_LOAD {
	uint16_t ulen;
	uint16_t clen;
	char addr[MSG_IPADDR_SIZE];
	char url[HTTP_URL_SIZE];
	char * cookie;
} TASK_LOAD;

int master_onpack(char * pack, int headlen, int packlen, 
					int scode, const void * obj)
{
	assert(pack && headlen == MSG_HEAD_LEN && packlen >= MSG_HEAD_LEN && obj);

//	SERVICE * p = (SERVICE * )obj;

	MSG_HEAD * head = (MSG_HEAD *)pack;
	msg_head_n2h(head);

	if (head->msgtype != TYPE_LOAD_REQ) {
		return -1;
	}

	pack += MSG_HEAD_LEN;
	packlen -= MSG_HEAD_LEN;

	MSG_LOAD_REQ * body = (MSG_LOAD_REQ *)pack;
	msg_load_req_n2h(body);

	int num = body->num;
	if (num == 0) {
		return 0;
	}

	MSG_LOAD_ITEM * item = (MSG_LOAD_ITEM *)body->vbuf;
	while (num --) {
		msg_load_item_n2h(item);

		if (item->ulen < 0 || item->ulen > HTTP_URL_SIZE - 1) {
			return -2;
		}

		TASK * task = malloc(sizeof(struct TASK));
		if (!task) {
			return -3;
		}

		TASK_LOAD * data = malloc(sizeof(struct TASK_LOAD));
		if (!data) {
			free(task);
			return -4;
		}

		data->ulen = item->ulen;
		data->clen = item->clen;
		memcpy(data->addr, item->addr, MSG_IPADDR_SIZE);
		memcpy(data->url, item->vbuf, data->ulen);
		data->url[data->ulen] = 0;
		data->cookie = NULL;
		task->data = data;

		int vlen = data->ulen + data->clen;
		if (vlen & 1) { vlen ++; }
		item = (void * )item + sizeof(struct MSG_LOAD_ITEM) + vlen;

		service_dispatch(task);
	}
	//check packlen
	return 0;
}

int master_ontask(TASK * task, const THREAD * master)
{
	assert(task && master && master->sys);

//	SERVICE * p = (SERVICE *)master->sys;

	send_log(LV_DEBUG, "%s on response {%s}", TTAG(master), (char *)task->data);

	free(task->data);
	free(task);
	return 0;
}

int worker_onpack(char * pack, int headlen, int packlen, 
		int scode, const void * obj)
{
	assert(pack && headlen > 0 && packlen >= headlen && obj);

	THREAD * p = (THREAD * )obj;

	send_log(LV_DEBUG, "%s on packet {%s}", TTAG(p), (char *)pack);
	return 0;
}

int worker_ontask(TASK * task, const THREAD * worker)
{
	assert(task && worker);

	TASK_LOAD * data = (TASK_LOAD *)task->data;

	send_log(LV_DEBUG, "%s on request {url: %s, ip: %s}", TTAG(worker), 
				data->url, data->addr);

	struct sockaddr_in addr;

	int sfd = client_socket(data->addr, 80, &addr);
	if(sfd < 0){
		send_log(LV_ERROR, "%s client_socket %s", TTAG(worker), data->addr);
		return -1;
	}
	int len;
	char * query = http_query_new(data->url, NULL, &len);
	if(!query){
		send_log(LV_ERROR, "%s http_query_new", TTAG(worker));
		return -2;
	}

	CONN * c = conn_new(sfd, &addr, conn_connecting, EV_WRITE | EV_PERSIST,
		worker->base, http_depack, worker_onpack, worker);
	if (c == NULL){
		send_log(LV_ERROR, "%s conn_new", TTAG(worker));
		return -3;
	}
	conn_wlist_add(c, query, len);

	free(task->data);
	free(task);

	//send response
	return 0;
}

int main(int argc, char ** argv)
{
	const char * cfg_path = NULL;
	char c;
	while ((c = getopt(argc, argv, "f:vh")) != -1) {
		switch(c){
		case 'f':
			cfg_path = optarg;
			break;
		case 'h':
			fprintf(stdout, USAGE, argv[0]);
			exit(0);
		case 'v':
			version(MODULE);
			exit(0);
		default:
			fprintf(stderr, USAGE, argv[0]);
			exit(1);
		}
	}
	
	CFGMAP * cfg = cfg_new();
	if (!cfg) exit(2);

	if (cfg_path && cfg_load(cfg, cfg_path) < 0) {
		fprintf(stderr, USAGE, argv[0]);
		exit(3);
	} else if (cfg_load(cfg, "loader.ini") < 0) {
		fprintf(stderr, "loader.ini not found");
		exit(4);
	}
	
	cfg_dump(cfg);
	
	unsigned int conns_num = cfg_get_int(cfg, "core:conns_num", 0);
	if (!conns_num) exit(5);

	int i;
	for (i = 0; i < conns_num; i ++) {
		char key[32];

		sprintf(key, "conn%d:ip", i);
		const char * ip = cfg_get_str(cfg, key, NULL);

		sprintf(key, "conn%d:port", i);
		int port = cfg_get_int(cfg, key, 0);

		if (!ip || !port) exit(6);

		service_add_iface(TYPE_SERVER, ip, port, msg_depack, master_onpack);
	}

	unsigned int workers_num = cfg_get_int(cfg, "core:workers_num", 0);
	if (!workers_num) exit(7);

	service_add_workers(workers_num, worker_ontask, NULL, NULL);

	service_set_master(master_ontask, NULL, NULL);

	service_set_log_level(cfg_get_int(cfg, "log:level", LV_ERROR));

	service_set_log(cfg_get_str(cfg, "log:fatal", NULL), 
		LV_FATAL, cfg_get_int(cfg, "log:fatal_size", 1024));

	service_set_log(cfg_get_str(cfg, "log:error", NULL), 
		LV_ERROR, cfg_get_int(cfg, "log:error_size", 1024));

	service_set_log(cfg_get_str(cfg, "log:sys", NULL), 
		LV_SYS, cfg_get_int(cfg, "log:sys_size", 1024));

	service_set_log(cfg_get_str(cfg, "log:warn", NULL), 
		LV_WARN, cfg_get_int(cfg, "log:warn_size", 1024));

	service_set_log(cfg_get_str(cfg, "log:info", NULL), 
		LV_INFO, cfg_get_int(cfg, "log:info_size", 1024));

	service_set_log(cfg_get_str(cfg, "log:debug", NULL), 
		LV_DEBUG, cfg_get_int(cfg, "log:debug_size", 1024));

	service_set_pid_path(cfg_get_str(cfg, "core:pid_path", NULL));


	service_run();
	return 0;
}

